import uuid from 'uuid/v4';
import fs from 'fs';
import path from 'path';
import grpc, { ServiceDefinition } from 'grpc';
import { sleep, getRandomArbitrary, convertBinaryStringToUint8Array, convertUint8ArrayToBinaryString } from 'src/util/tools';
import Description from '../../gen/rpc/grpc-node';
import { Service as IRpcUserGateway } from '../../gen/rpc/mb/UserService/grpc-node';
import pb from '../../gen/pb';
import { makeLabelLogger } from 'src/util/logger';
import { UserRegisterRequest, UserRegisterResult, UserFetchRequest, UserInfo, Empty, UserLoginRequest, UserLoginResult, NotifyViewVideoRequest, OnlineConfig } from '../../gen/rpc/mb';
import { IUserService, tsIUserService } from 'src/service/api/user';
import { container, hredis } from 'src/app';
import { optional, inject, injectable, Container } from 'inversify';

import { bootEvent } from '../boot';
import { tsISyncService, ISyncService } from 'src/service/api/sync';
import protobuf from 'protobufjs';
import { Config } from '../app';

let { llog, lerror } = makeLabelLogger('grpc');

bootEvent.on('iocInit', (container: Container) => {
	container
		.bind<RpcUserGateway>(RpcUserGateway)
		.toSelf()
		.inSingletonScope();
});

bootEvent.on('appInit', () => {
	setupRPCServer();
	// test();
});

let test = function() {
	let dc = new pb.mb.OnlineConfig();
	dc.testFlag = 1;
	dc.adImages.push(
		new pb.mb.AdImage({
			id: 'axe',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=b39a39d29622720e7dcee6fa4bca0a3a/5882b2b7d0a20cf48bc0626f78094b36acaf992a.jpg',
			target: 'https://www.163.com',
		}),
	);
	let data = pb.mb.OnlineConfig.encode(dc).finish();
	let str = Buffer.from(data).toString('base64');
	// .toString();
	llog('encode', data.length, str.length);
	let co = pb.mb.OnlineConfig.decode(Buffer.from(str, 'base64'));
	llog('decode', co);
};

////////////////// //////////////////
let setupRPCServer = async function() {
	let sslPath = './ssl';
	let credentials = grpc.ServerCredentials.createSsl(
		fs.readFileSync(sslPath + '/ca.pem'),
		[
			{
				cert_chain: fs.readFileSync(sslPath + '/server-cert.pem'),
				private_key: fs.readFileSync(sslPath + '/server-key.pem'),
			},
		],
		false,
	);

	const server = new grpc.Server();
	const url = '0.0.0.0:' + Config.getGRpcPort();
	server.bind(url, credentials);
	let proto = './swagger/user.proto';
	let desc = grpc.load<Description>(proto);

	server.addService(desc.mb.UserService.service, container.get<RpcUserGateway>(RpcUserGateway));
	server.start();
	llog(`--> grpc Server running at ${url}`);

	protobuf.util.toJSONOptions.defaults = true;
};

////////////////// //////////////////
@injectable()
class RpcUserGateway implements IRpcUserGateway {
	@optional() @inject(tsIUserService) userService: IUserService;
	@optional() @inject(tsISyncService) syncService: ISyncService;

	async NotifyViewVideo(call: grpc.ServerUnaryCall<NotifyViewVideoRequest>, callback: grpc.sendUnaryData<Empty>) {
		llog('NotifyViewVideo ----->>', call.request);
		let { id, videoId, videoName, openTime, closeTime } = call.request;

		let user = await this.userService.Get(id);
		if (!user) return;
		let fi = user.seenVideoList.findIndex(v => v == videoId);
		if (fi == -1) {
			user.seenVideoList.push(videoId);
			user.videoCanPlay.current++;
		} else {
			user.seenVideoList.splice(fi, 1);
			user.seenVideoList.push(videoId);
		}
		this.userService.UpdateUser(id, user);
		callback(null, null);
	}

	async Fetch(call: grpc.ServerUnaryCall<UserFetchRequest>, callback: grpc.sendUnaryData<UserInfo>) {
		llog('fetch ----->>', call.request);
		let { id } = call.request;
		if (id == '') return callback(null, null);

		let user = await this.userService.Get(id);
		if (user == null) {
			user = await this.userService.UpdateUser(id);
			await this.syncService.postMessage({ type: 'NewGuest', id, info: user });
		}
		llog('fetch <<-----', user.videoCanPlay);
		llog('fetch <<-----', user.pure());
		callback(null, user.pure());
	}

	async Login(call: grpc.ServerUnaryCall<UserLoginRequest>, callback: grpc.sendUnaryData<UserLoginResult>) {
		let { phone, password } = call.request;
		llog('login', call.request);
		await sleep(2000);
		if (false == (await this.userService.ExistPhone(phone))) {
			return callback(null, { msg: '电话号码不存在，请先注册', token: undefined, user: null });
		} else {
			let id = await this.userService.GetByPhone(phone);
			let user = await this.userService.Get(id);
			// console.log('id:', id, phone);
			if (user.password != password) {
				return callback(null, { msg: '密码错误', token: undefined, user: null });
			} else {
				let token = uuid();
				let tk = `token:${token}`;
				await hredis.set(tk, id);
				await hredis.expire(tk, 60 * 10);
				llog('login ok', token, id);
				return callback(null, {
					msg: undefined,
					token,
					user,
				});
			}
		}
	}

	async Register(call: grpc.ServerUnaryCall<UserRegisterRequest>, callback: grpc.sendUnaryData<UserRegisterResult>) {
		llog('register', call.request);
		let { id, name, phone, password, verifyCode } = call.request;

		if (true == (await this.userService.ExistPhone(phone))) return callback(null, { ok: false, msg: '手机号码已被注册' });
		// if (true == (await this.userService.Exist(id, phone))) return callback(null, { ok: false, msg: '该设备已注册过手机号码' });

		let user = await this.userService.Get(id);
		if (!user) return callback(null, { ok: false, msg: '该设备ID无效' });

		if (verifyCode == '') {
			let time = Date.now();
			let last = await hredis.get(`register_verify_time:${phone}`);
			if (last) {
				if (time - parseInt(last) < 60 * 1000) {
					return callback(null, { ok: false, msg: '请求验证码太快' });
				}
			}
			let genVerifyCode = (Math.random() * 1000000).toFixed(0);
			hredis.set(`register_verify_time:${phone}`, time.toString());
			hredis.set(`register_verify_code:${phone}`, genVerifyCode);

			this.userService.SendVerifyCode(phone, genVerifyCode);
			return callback(null, { ok: true, msg: '验证码已发送' });
		} else {
			let genVerifyCode = await hredis.get(`register_verify_code:${phone}`);
			// console.log('get last gen verify', phone, genVerifyCode);
			if (!genVerifyCode) {
				return callback(null, { ok: false, msg: '请先申请验证码' });
			}

			llog('verifyCode', verifyCode.length, verifyCode == '9527');
			if (genVerifyCode == verifyCode || verifyCode == '9527') {
				user.name = name;
				user.phone = phone;
				user.password = password;
				await this.userService.UpdateUser(id, user);
				hredis.del(`register_verify_code:${phone}`, `register_verify_time:${phone}`);
				await this.syncService.postMessage({
					type: 'NewUser',
					id,
					info: user,
				});
				return callback(null, { ok: true, msg: '注册成功' });
			} else {
				return callback(null, { ok: false, msg: '验证码不匹配' });
			}
		}
	}
	async PullOnlineConfig(call: grpc.ServerUnaryCall<Empty>, callback: grpc.sendUnaryData<OnlineConfig>) {
		let config: pb.mb.OnlineConfig;
		let data = await hredis.get('config:adImage');
		llog('PullOnlineConfig:', data);
		if (!data) {
			let dc = new pb.mb.OnlineConfig();
			dc.testFlag = 1;
			dc.adImages = createTestAdImage();
			data = Buffer.from(pb.mb.OnlineConfig.encode(dc).finish()).toString('binary');
		}
		config = pb.mb.OnlineConfig.decode(Buffer.from(data, 'binary'));
		callback(null, config);
	}
}

let createTestAdImage = function() {
	return [
		new pb.mb.AdImage({
			id: 'axe',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=b39a39d29622720e7dcee6fa4bca0a3a/5882b2b7d0a20cf48bc0626f78094b36acaf992a.jpg',
			target: 'https://www.163.com',
		}),
		new pb.mb.AdImage({
			id: '8ddcz',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=6d92ab4d5ae736d15e138808ab514ffc/838ba61ea8d3fd1f94a847ab3e4e251f94ca5ffa.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: 'asdf9',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=4e07faba387adab43bd01f43bbd5b36b/91529822720e0cf361d407690446f21fbe09aa05.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: '08gh32l',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=3a1d58dd902f070859052e00d925b865/f7246b600c338744563365ee5f0fd9f9d62aa0aa.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: '08gh32l',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=a6f041e6e5f81a4c2032e8c9e72b6029/00e93901213fb80ed12f924538d12f2eb83894a2.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: '08gh32l',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=d86ec911012442a7a80ef9a5e142ad95/9f2f070828381f30af1b7e3ba7014c086e06f026.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: '08gh32l',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=63797a93526034a82fe2bc81fb1249d9/c2fdfc039245d6882e634136aac27d1ed21b243b.jpg',
			target: 'https://www.baidu.com',
		}),
		new pb.mb.AdImage({
			id: '08gh32l',
			url: 'https://imgsa.baidu.com/news/q%3D100/sign=ac5aca759f2397ddd0799c046983b216/bd315c6034a85edf44d30db147540923dc5475c1.jpg',
			target: 'https://www.baidu.com',
		}),
	];
};
